package com.gatico;

import com.alibaba.fastjson.JSONObject;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class NettyClient {
    static int tunId = 1;
    static String SERVER_DOMAIN = "gatico.com.cn";//"gatico.com.cn"
    static int SERVER_PORT = 10000;
    static String LOCAL_DOMAIN = "127.0.0.1";
    static int LOCAL_PORT = 8080;
    private final static ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    private static Channel channel;
    private static Bootstrap bootstrap;
    private static EventLoopGroup group;

    public static void main(String[] args) throws InterruptedException {
        if (args.length > 0) {
            tunId = Integer.parseInt(args[0]);
        }
        if (args.length > 1) {
            SERVER_DOMAIN = args[1];
        }
        if (args.length > 2) {
            SERVER_PORT = Integer.parseInt(args[2]);
        }
        if (args.length > 3) {
            LOCAL_DOMAIN = args[3];
        }
        if (args.length > 4) {
            LOCAL_PORT = Integer.parseInt(args[4]);
        }

        System.setProperty("jdk.httpclient.allowRestrictedHeaders",
                "Host,Connection,Pragma,Cache-Control,sec-ch-ua,sec-ch-ua-mobile,sec-ch-ua-platform,Upgrade-Insecure-Requests,User-Agent,Accept,Sec-Fetch-Site,Sec-Fetch-Mode,Sec-Fetch-User,Sec-Fetch-Dest,Referer,Accept-Encoding,Accept-Language,Cookie,Content-length,Content-type");
        group = new NioEventLoopGroup();
        try {
            bootstrap = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            // 设置读写空闲时间为5秒
                            ch.pipeline().addLast(new IdleStateHandler(0, 5, 0));
                            ch.pipeline().addLast(new ProxyDecoder(), new ProxyEncoder(), new ClientHandler());
                        }
                    });
//            doConnect();
//            // 连接到服务器
            ChannelFuture future = bootstrap.connect(SERVER_DOMAIN, SERVER_PORT).sync();
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
//            stop();
        }
    }

    private static void doConnect() {
        if (channel != null && channel.isActive()) {
            return;
        }

        ChannelFuture future = bootstrap.connect(SERVER_DOMAIN, SERVER_PORT);

        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    channel = future.channel();
                    System.out.println("Connect to server successfully!");
                } else {
                    System.out.println("Failed to connect to server, try to reconnect after 5 seconds...");
                    future.channel().eventLoop().schedule(NettyClient::doConnect, 5, TimeUnit.SECONDS);
                }
            }
        });
    }

    public static void stop() {
        if (channel != null) {
            channel.close();
        }
        group.shutdownGracefully();
        executor.shutdown();
    }

    private static class ClientHandler extends ChannelInboundHandlerAdapter {
        public void channelActive(ChannelHandlerContext ctx) {
            System.out.println("客户端与和服务端建立连接");
            Map<String, String> map = new HashMap<>();
            map.put("type", "init");
            map.put("token", "767977e03ad246d5abc19ef45ec05272");
            // 当连接建立时，发送消息到服务器
            ctx.writeAndFlush(ProxyProtocol.msgProtocol(JSONObject.toJSONString(map)));
        }

        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ProxyProtocol protocol = (ProxyProtocol) msg;
            if (protocol.type == ProxyProtocol.TYPE.HEART.value()) {
                // 心跳
                System.out.println("收到服务端心跳 ");
            }
            if (protocol.type == ProxyProtocol.TYPE.TCP.value()) {
                // tcp代理
                System.out.println("收到服务端数据，需要进行tcp代理:");
            }
            if (protocol.type == ProxyProtocol.TYPE.HTTP.value()) {
                ProxyProtocol response = new ProxyProtocol();
                response.type = ProxyProtocol.TYPE.HTTP.value();
                response.ip = protocol.ip;
                response.port = protocol.port;
                // http代理
                System.out.println("收到浏览器消息:" + protocol.length);
                System.out.println("收到浏览器消息:" + new String(protocol.data));
                System.out.println("收到服务端数据，需要进行http代理:");
                new Thread(() -> {
                    try {
//                        BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(protocol.data)));
//                        String requestLine = reader.readLine();
//                        if (requestLine == null) {
//                            return;
//                        }
//                        String[] requestParts = requestLine.split(" ");
//                        String method = requestParts[0];
//                        String path = requestParts[1];
//
//                        List<String> headers = new ArrayList<>();
//                        String headerLine;
//                        String url = "";
//                        while (!(headerLine = reader.readLine()).isEmpty()) {
//                            String key = headerLine.substring(0, headerLine.indexOf(":"));
//                            headers.add(key);
//                            String value = headerLine.substring(headerLine.indexOf(":") + 1);
//                            headers.add(value);
//                            if (key.equals("Host")) {
//                                url = value.trim();
//                            }
//                        }
//                        HttpClient client = HttpClient.newHttpClient();
//                        HttpRequest request = HttpRequest.newBuilder().version(HttpClient.Version.HTTP_1_1).uri(new URI("http://" + url + path)).headers(headers.toArray(new String[0])).method(method, HttpRequest.BodyPublishers.noBody()).build();
//                        HttpResponse<byte[]> send = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
//                        HttpHeaders resHeaders = send.headers();
//                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//                        outputStream.write(("HTTP/1.1" + " " + send.statusCode() + "\r\n").getBytes());
//                        resHeaders.map().forEach((k, v) -> {
//                            try {
//                                outputStream.write((k + ":" + String.join(";", v) + "\r\n").getBytes());
//                            } catch (IOException e) {
//                                e.printStackTrace();
//                            }
//                        });
//                        outputStream.write("\r\n".getBytes());
//                        outputStream.write(send.body());
//                        outputStream.flush();
//                        response.data = outputStream.toByteArray();
//                        System.out.println("响应的数据：" + new String(response.data));
//                        response.length = response.data.length;
//                        System.out.println("响应的数据长度：" + response.length);
//                        ctx.writeAndFlush(response);

//                        Socket socket = new Socket(IPv4Utils.intToStr(protocol.ip), protocol.port);
//                        socket.setKeepAlive(false);
//                        socket.setTcpNoDelay(true);
//                        socket.getOutputStream().write(protocol.data);
//                        socket.getOutputStream().flush();
////                        PrintStream targetPrintStream = new PrintStream(socket.getOutputStream(), true);
////                        targetPrintStream.println(new String(protocol.data));
//                        try {
//                            ByteArrayOutputStream os = new ByteArrayOutputStream();
//                            int len = 0;
//                            while ((len = socket.getInputStream().read()) > 0) {
//                                os.write(len);
//                                os.flush();
//                            }
//                            response.data = os.toByteArray();
//                            System.out.println("响应的数据：" + new String(response.data));
//                            response.length = response.data.length;
//                            System.out.println("响应的数据长度：" + response.length);
//                            ctx.writeAndFlush(response);
//                            socket.close();
//                        } catch (IOException e) {
//                            e.printStackTrace();
//                        }

                        ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(protocol.data));
                        HttpData httpReq = (HttpData) is.readObject();
                        List<String> headers = new ArrayList<>();
                        httpReq.getHeaders().forEach((key, value) -> {
                            headers.add(key);
                            headers.add(String.join(";", value));
                        });
                        System.out.println(JSONObject.toJSONString(httpReq));
                        HttpRequest request = HttpRequest.newBuilder()
                                .uri(new URI(httpReq.getUrl()))
                                .headers(headers.toArray(new String[0]))
                                .method(httpReq.getMethod(), HttpRequest.BodyPublishers.ofByteArray(httpReq.getBody()))
                                .build();
                        HttpClient client = HttpClient.newHttpClient();
                        HttpResponse<InputStream> send = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
                        HttpData httpRes = new HttpData();
                        System.out.println(JSONObject.toJSONString(send.headers().map()) );
                        httpRes.setHeaders(send.headers().map());
                        httpRes.setStatusCode(send.statusCode());
                        httpRes.setBody(send.body().readAllBytes());
                        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                        ObjectOutputStream os = new ObjectOutputStream(byteArrayOutputStream);
                        os.writeObject(httpRes);
                        os.flush();
                        response.data = byteArrayOutputStream.toByteArray();
                        response.length = response.data.length;
                        System.out.println("响应的数据长度：" + response.length);
                        ctx.writeAndFlush(response);
                    } catch (IOException e) {
                        HttpData httpRes = new HttpData();
                        httpRes.setStatusCode(500);
                        Map<String, List<String>> headers = new HashMap<>();
                        headers.put("content-type", List.of("text/html;charset=UTF-8"));
                        httpRes.setHeaders(headers);
                        httpRes.setBody("代理端口异常".getBytes());
                        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                        ObjectOutputStream os = null;
                        try {
                            os = new ObjectOutputStream(byteArrayOutputStream);
                            os.writeObject(httpRes);
                            os.flush();
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                        response.data = byteArrayOutputStream.toByteArray();
                        response.length = response.data.length;
                        System.out.println("响应的数据长度：" + response.length);
                        ctx.writeAndFlush(response);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (URISyntaxException e) {
                        HttpData httpRes = new HttpData();
                        httpRes.setStatusCode(500);
                        httpRes.setBody("请求URL异常".getBytes());
                        Map<String, List<String>> headers = new HashMap<>();
                        headers.put("content-type", List.of("text/html;charset=UTF-8"));
                        httpRes.setHeaders(headers);
                        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                        ObjectOutputStream os = null;
                        try {
                            os = new ObjectOutputStream(byteArrayOutputStream);
                            os.writeObject(httpRes);
                            os.flush();
                        } catch (IOException ex) {
                            throw new RuntimeException(ex);
                        }
                        response.data = byteArrayOutputStream.toByteArray();
                        response.length = response.data.length;
                        System.out.println("响应的数据长度：" + response.length);
                        ctx.writeAndFlush(response);
                    }
                }).start();
            }
            if (protocol.type == ProxyProtocol.TYPE.MSG.value()) {
                // 消息
                String message = new String(protocol.data);
                System.out.println("收到服务端消息:" + message);
                if("reconnect".equals(message)){

                }
            }
        }

        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof IdleStateEvent) {
                IdleStateEvent event = (IdleStateEvent) evt;
                if (event.state() == IdleState.WRITER_IDLE) {
                    // 写空闲，发送心跳消息
                    ctx.writeAndFlush(ProxyProtocol.heart());
                }
            } else {
                super.userEventTriggered(ctx, evt);
            }
        }
    }
}
